package com.flying.flow.service;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.flying.common.annotation.DaoCreate;
import com.flying.common.annotation.DaoQuery;
import com.flying.common.annotation.DaoRemove;
import com.flying.common.annotation.DaoUpdate;
import com.flying.common.annotation.Transaction;
import com.flying.common.util.Codes;
import com.flying.common.util.Utils;
import com.flying.flow.model.FlowDefinition;
import com.flying.flow.model.FlowDefinition.PublishStatus;
import com.flying.flow.model.Node;
import com.flying.flow.model.Node.AllowSplit;
import com.flying.flow.model.Node.FinishCondition;
import com.flying.flow.model.Node.NodeType;
import com.flying.flow.model.Node.RouteMode;
import com.flying.flow.model.Node.TaskMode;
import com.flying.flow.model.Node.TaskStartType;
import com.flying.flow.model.NodeTransition;
import com.flying.framework.annotation.MethodInfo;
import com.flying.framework.annotation.Param;
import com.flying.framework.annotation.Service;
import com.flying.framework.context.ServiceContext;
import com.flying.framework.exception.AppException;
import com.flying.framework.model.ActionResult;
import com.flying.framework.model.ModelFactory;
import com.flying.framework.model.QueryResult;
import com.flying.framework.security.Principal;
import com.flying.framework.service.AbstractService;

@Service(value="FlowDefinitionService", desc="流程定义")
public class FlowDefinitionService extends AbstractService{
	private NodeService ns;
	private NodeTransitionService nts;
	
	public void setNodeService(NodeService ns) {
		this.ns = ns;
	}
	
	public void setNodeTransitionService(NodeTransitionService s) {
		this.nts = s;
	}
	
	@MethodInfo("新增")
	@Transaction
	public FlowDefinition create(
			@Param(value=FlowDefinition.ORG_CODE, required=false,	desc="机构") String org_code,
			@Param(value=FlowDefinition.BUSINESS_TYPE, required=false,	desc="业务") String business_type,
			@Param(value=FlowDefinition.NAME, required=false,	desc="名称") String name,
			@Param(value=FlowDefinition.CODE, required=false,	desc="代码") String code,
			@Param(value=FlowDefinition.REMARKS, required=false,	desc="说明") String remarks,
			@Param(value=FlowDefinition.VERSION, required=false,	desc="流程版本") Integer version
		) throws Exception {
		FlowDefinition fd = this.createfd(org_code, business_type, name, code, remarks, version, null, null);
		this.ns.create2(fd.getFlowDefId(), "Start", "Start Node", 
				TaskMode.ONE_TASK_ONE_PARTICIPANT.value(),AllowSplit.ALLOW.value(),RouteMode.AUTO.value(),TaskStartType.AUTO.value(), 
				NodeType.START.value(), FinishCondition.NONE.value());
		this.ns.create2(fd.getFlowDefId(), "End", "End Node", 
				TaskMode.ONE_TASK_ONE_PARTICIPANT.value(),AllowSplit.ALLOW.value(),RouteMode.AUTO.value(), TaskStartType.AUTO.value(), 
				NodeType.END.value(), FinishCondition.NONE.value());
		return fd;
	}
	
	@MethodInfo("新增")
	@DaoCreate(entity=FlowDefinition.ENTITY_NAME)
	protected FlowDefinition createfd(
			@Param(value=FlowDefinition.ORG_CODE, required=false,	desc="机构") String org_code,
			@Param(value=FlowDefinition.BUSINESS_TYPE, required=false,	desc="业务") String business_type,
			@Param(value=FlowDefinition.NAME, required=true,	desc="名称") String name,
			@Param(value=FlowDefinition.CODE, required=false,	desc="代码") String code,
			@Param(value=FlowDefinition.REMARKS, required=false,	desc="说明") String remarks,
			@Param(value=FlowDefinition.VERSION, required=false,	desc="流程版本") Integer version,
			@Param(value=FlowDefinition.CREATOR, required=false,	desc="创建人") String creator,
			@Param(value=FlowDefinition.CREATE_TIME, required=false,	desc="创建时间") Date create_time
		) throws Exception {
		//
		Principal p = ServiceContext.getContext().getPrincipal();
		return ModelFactory.create(FlowDefinition.class, 
				FlowDefinition.VERSION, version, 
				FlowDefinition.CREATOR, (p == null?null: p.getName()),
				FlowDefinition.CREATE_TIME, new Date());
	}
	
	@MethodInfo("克隆")
	@Transaction
	public FlowDefinition cloneFlowDefinition(
			@Param(value=FlowDefinition.FLOW_DEF_ID, required=true,	desc="流程定义ID") Long flow_def_id
		) throws Exception {
		FlowDefinition sfd = this.findById(flow_def_id);
		List<Node> snods = ns.findByFlowDefinitionId(flow_def_id).getRows();
		List<NodeTransition> snodeTrans = nts.findByFlowDefinitionId(flow_def_id).getRows();
		
		FlowDefinition nfd = this.create(sfd.getOrgCode(), sfd.getBusinessType(), sfd.getName(), sfd.getCode(), sfd.getRemarks(), sfd.getVersion() + 1);
		List<Node> nnods = ns.findByFlowDefinitionId(nfd.getFlowDefId()).getRows();
		Map<Long, Node> nmap = Utils.newHashMap();
		for(Node sn: snods) {
			if(sn.getNodeType() == NodeType.START.value()) {
				nnods.stream().filter(x -> x.getNodeType() == NodeType.START.value()).forEach(x -> {nmap.put(sn.getNodeId(), x); });
			} else if(sn.getNodeType() == NodeType.END.value()) {
				nnods.stream().filter(x -> x.getNodeType() == NodeType.END.value()).forEach(x -> {nmap.put(sn.getNodeId(), x); });
			} else {
				Node nn = this.ns.cloneNode(nfd.getFlowDefId(), sn.getNodeId());
				nmap.put(sn.getNodeId(), nn);
			}
		}
		for(NodeTransition x: snodeTrans) {
			Long from_node_id = nmap.get(x.getFromNodeId()).getNodeId();
			Long to_node_id = nmap.get(x.getToNodeId()).getNodeId();
			this.nts.create2(nfd.getFlowDefId(), x.getName(), x.getRemarks(), from_node_id, to_node_id, x.getCondition());
		};
		return nfd;
	}

	@MethodInfo("修改")
	@DaoUpdate(entity=FlowDefinition.ENTITY_NAME)
	public ActionResult update(
			@Param(value=FlowDefinition.FLOW_DEF_ID, required=true,	desc="流程定义ID") Long flow_def_id,
			@Param(value=FlowDefinition.ORG_CODE, required=false,	desc="机构") String org_code,
			@Param(value=FlowDefinition.BUSINESS_TYPE, required=false,	desc="业务") String business_type,
			@Param(value=FlowDefinition.NAME, required=true,	desc="名称") String name,
			@Param(value=FlowDefinition.CODE, required=false,	desc="代码") String code,
			@Param(value=FlowDefinition.REMARKS, required=false,	desc="说明") String remarks,
			@Param(value=FlowDefinition.VERSION, required=false,	desc="流程版本") Integer version,
			@Param(value=FlowDefinition.UPDATOR, required=false,	desc="修改人") String updator,
			@Param(value=FlowDefinition.UPDATE_TIME, required=false,	desc="修改时间") Date update_time
		) throws Exception {
		Principal p = ServiceContext.getContext().getPrincipal();
		return ModelFactory.create(ActionResult.class, 
				FlowDefinition.UPDATOR, (p == null?null: p.getName()),
				FlowDefinition.UPDATE_TIME, new Date());
	}

	@MethodInfo("检查是否允许修改")
	public ActionResult checkUpdatable(
			@Param(value=FlowDefinition.FLOW_DEF_ID, required=true,	desc="流程定义ID") Long flow_def_id) throws Exception {
		FlowDefinition fd = this.findById(flow_def_id);
		if(PublishStatus.PUBLISHED.value().equals(fd.getPublishStatus())) {
			throw new AppException("0001", "流程已经发布，不能修改！");
		}
		return ModelFactory.create(ActionResult.class, 
				Codes.CODE, Codes.SUCCESS);
	}

	@MethodInfo("检查合法性")
	public ActionResult checkValidation(
			@Param(value=FlowDefinition.FLOW_DEF_ID, required=true,	desc="流程定义ID") Long flow_def_id) throws Exception {
		FlowDefinition fd = this.findById(flow_def_id);
		List<Node> nodes = fd.getNodes();
		Set<Long> nodeIds = Utils.newHashSet();
		nodes.stream().forEach(x -> nodeIds.add(x.getNodeId()));
		List<NodeTransition> trans = fd.getTransitions();
		//所有Transition正确
		trans.stream().forEach(x ->{
			if(!nodeIds.contains(x.getFromNodeId()) || 
					!nodeIds.contains(x.getToNodeId()) || 
					x.getFromNodeId().equals(x.getToNodeId())) {
				throw new AppException("0001", "流程Transition不合理（FROM："+x.getFromNodeId()+"；TO："+x.getToNodeId()+"）！");
			}
		});
		//节点
		nodes.stream().forEach(x -> {
			int inCount = 0;
			int outCount = 0;
			for(NodeTransition t: trans) {
				if(t.getFromNodeId().equals(x.getNodeId())) {
					outCount ++;
				}
				if(t.getToNodeId().equals(x.getNodeId())) {
					inCount ++;
				}
			}
			if(x.getNodeType().equals(NodeType.START.value())) {	//Start有出口，无入口
				if(outCount == 0 || inCount > 0) {
					throw new AppException("0001", "流程Start不合理（In："+inCount+"；Out："+outCount+"）！");
				}
			}else if(x.getNodeType().equals(NodeType.END.value())) {	//End有入口，无出口
				if(outCount > 0 || inCount == 0) {
					throw new AppException("0001", "流程End不合理（In："+inCount+"；Out："+outCount+"）！");
				}
			} else {
				if(outCount == 0 || inCount == 0) {
					throw new AppException("0001", "流程节点"+x.getName()+"不合理（In："+inCount+"；Out："+outCount+"）！");
				}
			}
		});
		
		return ModelFactory.create(ActionResult.class, 
				Codes.CODE, Codes.SUCCESS);
	}
	
	@MethodInfo("发布")
	@Transaction
	@DaoUpdate(entity=FlowDefinition.ENTITY_NAME)
	public ActionResult publish(
			@Param(value=FlowDefinition.FLOW_DEF_ID, required=true,	desc="流程定义ID") Long flow_def_id,
			@Param(value=FlowDefinition.PUBLISH_STATUS, required=false, enumClass=PublishStatus.class,	desc="发布状态") Integer publish_status,
			@Param(value=FlowDefinition.PUBLISHER, required=false,	desc="发布人") String publisher,
			@Param(value=FlowDefinition.PUBLISH_TIME, required=false,	desc="发布时间") Date publish_time
		) throws Exception {
		this.checkValidation(flow_def_id);
		Principal p = ServiceContext.getContext().getPrincipal();
		return ModelFactory.create(ActionResult.class, 
				FlowDefinition.PUBLISH_STATUS, PublishStatus.PUBLISHED.value(),
				FlowDefinition.PUBLISHER, (p == null?null: p.getName()),
				FlowDefinition.PUBLISH_TIME, new Date());
	}

	@MethodInfo("删除")
	@Transaction
	@DaoRemove(entity=FlowDefinition.ENTITY_NAME)
	public ActionResult remove(
			@Param(value=FlowDefinition.FLOW_DEF_ID, required=true,	desc="流程定义ID") Long flow_def_id
		) throws Exception {
		this.checkUpdatable(flow_def_id);
		this.nts.removeByFlowDefinitionId(flow_def_id);
		this.ns.removeByFlowDefinitionId(flow_def_id);
		return null;
	}

	@MethodInfo("搜索")
	@DaoQuery(entity=FlowDefinition.ENTITY_NAME, pageable=true, modelClass=FlowDefinition.class)
	public QueryResult<FlowDefinition> findAll(
			@Param(value=FlowDefinition.ORG_CODE, required=false,	desc="机构") String org_code,
			@Param(value=FlowDefinition.BUSINESS_TYPE, required=false,	desc="业务") String business_type,
			@Param(value=FlowDefinition.NAME, required=false,	desc="名称") String name,
			@Param(value=FlowDefinition.CODE, required=false,	desc="代码") String code,
			@Param(value=Codes.PAGE_NUM, 	required=false, desc="分页页号，默认0") int page,
			@Param(value=Codes.PAGE_SIZE, 	required=false, desc="分页大小，默认10") int rows) throws Exception {
		return null;
	}

	@MethodInfo("查询")
	@DaoQuery(entity=FlowDefinition.ENTITY_NAME, single=true, throwsNotFoundException=true )
	public FlowDefinition findById(
			@Param(value=FlowDefinition.FLOW_DEF_ID, required=true,	desc="流程定义ID") Long flow_def_id
		) throws Exception {
		return null;
	}

}